package com.zxp.mall.seckill.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.zxp.mall.common.utils.R;
import com.zxp.mall.seckill.feign.CouponFeignService;
import com.zxp.mall.seckill.feign.ProductFeignService;
import com.zxp.mall.seckill.service.SeckillService;
import com.zxp.mall.seckill.to.SeckillSkuRedisTo;
import com.zxp.mall.seckill.vo.SeckillSessionsWithSkus;
import com.zxp.mall.seckill.vo.SkuInfoVo;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class SeckillServiceImpl implements SeckillService {

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    private final String SESSIONS_CACHE_PREFIX = "seckill:sessions:";

    private final String SKUKILL_CACHE_PREFIX = "seckill:skus:";

    private final String SKU_STOCK_SEMAPHORE = "seckill:stock:"; //商品随机码

    @Override
    public void uploadSeckillSkuLatest3Days() {
//        1.扫描最近三天需要参加秒杀的活动和商品
        R r = couponFeignService.getLates3DaysSession();
        if (r.getCode() == 0) {
//            成功
            List<SeckillSessionsWithSkus> sessions = r.getData(new TypeReference<List<SeckillSessionsWithSkus>>() {
            });
//            缓存到redis信息 活动信息 活动对应的商品信息
            System.out.println(sessions.size());
            this.saveSessionInfos(sessions);

            this.saveSessionSkuInfos(sessions);
        }
    }

    //    缓存活动信息
    public void saveSessionInfos(List<SeckillSessionsWithSkus> sessions) {
        if (!CollectionUtils.isEmpty(sessions)) {

            sessions.stream().forEach(session -> {
                Long startTime = session.getStartTime().getTime();
                Long endTime = session.getEndTime().getTime();
                String key = SESSIONS_CACHE_PREFIX + startTime + "_" + endTime;
                if (!redisTemplate.hasKey(key)) {
                    List<String> skuIds =
                            session.getRelationSkus().stream().map(item -> item.getSkuId().toString()).collect(Collectors.toList());
                    redisTemplate.opsForList().leftPushAll(key, skuIds);
                }
            });
        }
    }

    //    缓存活动商品信息
    public void saveSessionSkuInfos(List<SeckillSessionsWithSkus> sessions) {
        if (!CollectionUtils.isEmpty(sessions)) {
            sessions.stream().forEach(session -> {
                BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);
                session.getRelationSkus().stream().forEach(item -> {
                    SeckillSkuRedisTo skuRedisTo = new SeckillSkuRedisTo();
                    BeanUtils.copyProperties(item, skuRedisTo);
                    R r = productFeignService.getSkuInfo(item.getSkuId());
                    if (r.getCode() == 0) {
//                sku的基本信息
                        SkuInfoVo skuInfo = r.getData("skuInfo", new TypeReference<SkuInfoVo>() {
                        });
                        skuRedisTo.setSkuInfo(skuInfo);
                    }
//                设置当前商品的秒杀时间信息
                    skuRedisTo.setStartTime(session.getStartTime().getTime());
                    skuRedisTo.setEndTime(session.getEndTime().getTime());
//                随机码
                    String code = UUID.randomUUID().toString().replace("-", "");
                    skuRedisTo.setRandomCode(code);
                    RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE + code);
//                可以秒杀的数量作为信号量
                    semaphore.trySetPermits(skuRedisTo.getSeckillCount());

                    ops.put(item.getSkuId().toString(), JSON.toJSONString(skuRedisTo));
                });
            });
        }
    }
}
